package org.linloong.utils;

import com.vladsch.flexmark.ext.tables.TablesExtension;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.jsoup.Jsoup;
import org.jsoup.nodes.*;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.HashMap;

/**
 * Markdown转Word工具类
 * 功能：将Markdown格式的文本转换为Microsoft Word文档(.docx格式)
 * 主要特性：
 * - 支持标题(h1-h6)、段落、列表、代码块、表格
 * - 支持网络图片下载并插入到文档中
 * - 使用Flexmark解析Markdown，Jsoup处理HTML
 * 
 * @author dbo
 * @description 该工具类用于将Markdown转换为Word文档
 */
@Service
@Slf4j
public class MdToWordUtil {

    // Markdown解析器和HTML渲染器，支持表格扩展
    private final Parser parser;
    private final HtmlRenderer renderer;
    
    // 图片格式映射，用于根据文件扩展名确定POI图片格式
    private static final Map<String, Integer> IMAGE_FORMAT_MAP = new HashMap<>();

    static {
        // 初始化图片格式映射
        IMAGE_FORMAT_MAP.put("png", XWPFDocument.PICTURE_TYPE_PNG);
        IMAGE_FORMAT_MAP.put("jpg", XWPFDocument.PICTURE_TYPE_JPEG);
        IMAGE_FORMAT_MAP.put("jpeg", XWPFDocument.PICTURE_TYPE_JPEG);
        IMAGE_FORMAT_MAP.put("gif", XWPFDocument.PICTURE_TYPE_GIF);
        IMAGE_FORMAT_MAP.put("bmp", XWPFDocument.PICTURE_TYPE_BMP);
    }

    /**
     * 构造函数：初始化Markdown解析器和HTML渲染器
     * 配置支持表格扩展的Flexmark解析器
     */
    public MdToWordUtil() {
        var extensions = List.of(TablesExtension.create());
        parser = Parser.builder().extensions(extensions).build();
        renderer = HtmlRenderer.builder().extensions(extensions).build();
    }

    /**
     * 将Markdown文本转换为Word文档字节数组
     * 
     * @param markdown Markdown格式的文本内容
     * @return Word文档的字节数组，可用于文件下载或存储
     * @throws Exception 转换过程中的异常
     */
    public byte[] convertMarkdownToWord(String markdown) throws Exception {
        // 处理null输入
        if (markdown == null) {
            markdown = "";
        }

        // 步骤1: Markdown -> HTML
        var document = parser.parse(markdown);
        String html = renderer.render(document);

        // 步骤2: HTML -> JSoup文档对象
        Document htmlDoc = Jsoup.parse(html);

        // 步骤3: HTML -> Word文档
        try (XWPFDocument wordDoc = new XWPFDocument()) {
            // 遍历HTML文档的所有子元素，逐个处理
            for (Element el : htmlDoc.body().children()) {
                processElement(wordDoc, el);
            }

            // 步骤4: Word文档 -> 字节数组
            try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
                wordDoc.write(out);
                return out.toByteArray();
            }
        }
    }

    /**
     * 处理HTML元素，根据元素类型调用相应的处理方法
     * 
     * @param doc Word文档对象
     * @param el HTML元素
     */
    private void processElement(XWPFDocument doc, Element el) {
        String tagName = el.tagName();
        
        switch (tagName) {
            case "h1", "h2", "h3", "h4", "h5", "h6" -> addHeading(doc, el);
            case "p" -> handleParagraph(doc, el);
            case "ul" -> addUnorderedList(doc, el);
            case "ol" -> addOrderedList(doc, el);
            case "pre" -> addCodeBlock(doc, el);
            case "table" -> addTable(doc, el);
            default -> addParagraph(doc, el.text()); // 未知元素按段落处理
        }
    }

    /**
     * 处理段落元素，特殊处理包含图片的段落
     * 
     * @param doc Word文档对象
     * @param p HTML段落元素
     */
    private void handleParagraph(XWPFDocument doc, Element p) {
        Elements imgs = p.select("img");
        if (!imgs.isEmpty()) {
            // 如果段落包含图片，逐个添加图片
            for (Element img : imgs) {
                addImage(doc, img);
            }
        } else {
            // 普通段落文本
            addParagraph(doc, p.text());
        }
    }

    /**
     * 添加标题到Word文档
     * 根据标题级别(h1-h6)设置不同的字体大小
     * 
     * @param doc Word文档对象
     * @param el HTML标题元素
     */
    private void addHeading(XWPFDocument doc, Element el) {
        int level = Integer.parseInt(el.tagName().substring(1)); // 提取标题级别
        XWPFParagraph p = doc.createParagraph();
        XWPFRun run = p.createRun();
        run.setText(el.text());
        run.setBold(true);
        run.setFontSize(Math.max(24 - level * 2, 12));
    }

    /**
     * 添加普通段落到Word文档
     * 
     * @param doc Word文档对象
     * @param text 段落文本内容
     */
    private void addParagraph(XWPFDocument doc, String text) {
        XWPFParagraph p = doc.createParagraph();
        XWPFRun run = p.createRun();
        run.setText(text);
    }

    /**
     * 添加无序列表到Word文档
     * 使用"•"符号作为列表标记
     * 
     * @param doc Word文档对象
     * @param ul HTML无序列表元素
     */
    private void addUnorderedList(XWPFDocument doc, Element ul) {
        for (Element li : ul.select("li")) {
            XWPFParagraph p = doc.createParagraph();
            XWPFRun run = p.createRun();
            run.setText("• " + li.text());
        }
    }

    /**
     * 添加有序列表到Word文档
     * 自动生成序号(1. 2. 3. ...)
     * 
     * @param doc Word文档对象
     * @param ol HTML有序列表元素
     */
    private void addOrderedList(XWPFDocument doc, Element ol) {
        int index = 1;
        for (Element li : ol.select("li")) {
            XWPFParagraph p = doc.createParagraph();
            XWPFRun run = p.createRun();
            run.setText(index++ + ". " + li.text());
        }
    }

    /**
     * 添加代码块到Word文档
     * 使用等宽字体(Courier New)和灰色文字
     * 
     * @param doc Word文档对象
     * @param pre HTML代码块元素
     */
    private void addCodeBlock(XWPFDocument doc, Element pre) {
        XWPFParagraph p = doc.createParagraph();
        XWPFRun run = p.createRun();
        run.setFontFamily("Courier New");
        run.setColor("555555");
        run.setText(pre.text());
    }

    /**
     * 添加表格到Word文档
     * 支持表头(th)和普通单元格(td)
     * 
     * @param doc Word文档对象
     * @param tableEl HTML表格元素
     */
    private void addTable(XWPFDocument doc, Element tableEl) {
        Elements rows = tableEl.select("tr");
        if (rows.isEmpty()) {
            return;
        }
        
        // 获取列数（以第一行为准）
        int cols = rows.get(0).select("td,th").size();
        XWPFTable table = doc.createTable(rows.size(), cols);
        
        // 填充表格内容
        for (int i = 0; i < rows.size(); i++) {
            Elements cells = rows.get(i).select("td,th");
            for (int j = 0; j < cells.size(); j++) {
                table.getRow(i).getCell(j).setText(cells.get(j).text());
            }
        }
    }

    /**
     * 添加图片到Word文档
     * 支持网络图片下载，自动识别图片格式
     * 
     * @param doc Word文档对象
     * @param imgEl HTML图片元素
     */
    private void addImage(XWPFDocument doc, Element imgEl) {
        String url = imgEl.attr("src");
        if (url == null || url.isEmpty()) {
            return;
        }

        try (InputStream is = downloadImage(url)) {
            if (is == null) {
                log.error("图片加载失败：{}", url);
                return;
            }

            // 根据URL后缀确定图片格式
            int format = getImageFormat(url);
            
            // 添加图片到文档，固定尺寸400x250像素
            XWPFParagraph p = doc.createParagraph();
            XWPFRun run = p.createRun();
            run.addPicture(is, format, url, Units.toEMU(400), Units.toEMU(250));
        } catch (Exception e) {
            log.error("图片加载失败：{} - {}", url, e.getMessage());
        }
    }

    /**
     * 根据URL确定图片格式
     * 
     * @param url 图片URL
     * @return POI图片格式常量
     */
    private int getImageFormat(String url) {
        String lower = url.toLowerCase();
        String extension = lower.substring(lower.lastIndexOf('.') + 1);
        return IMAGE_FORMAT_MAP.getOrDefault(extension, XWPFDocument.PICTURE_TYPE_PNG);
    }

    /**
     * 下载网络图片
     * 设置用户代理和超时时间，处理网络异常
     * 
     * @param imageUrl 图片URL
     * @return 图片输入流，失败返回null
     */
    private InputStream downloadImage(String imageUrl) {
        try {
            URL url = new URL(imageUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestProperty("User-Agent", "Mozilla/5.0"); // 模拟浏览器访问
            conn.setConnectTimeout(5000); // 连接超时5秒
            conn.setReadTimeout(5000); // 读取超时5秒

            if (conn.getResponseCode() == 200) {
                return conn.getInputStream();
            }
        } catch (Exception e) {
            log.error("图片下载失败：{} - {}", imageUrl, e.getMessage());
        }
        return null;
    }
}